package com.shermanzhou.nio;

import org.junit.Test;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.time.LocalDateTime;
import java.util.Iterator;
import java.util.Scanner;

/**
 * NIO 完成网络通信的三个核心：
 * 1. 通道（Channel）: 负责链接
 *     java.nio.channels.Channel 接口
 *          |-- SelectableChannel 抽象类
 *              |-- SocketChannel
 *              |-- ServerSocketChannel
 *              |-- DatagramChannel
 *
 *              |-- Pipe.SinkChannel
 *              |-- Pipe.SourceChannel
 *
 * 2. 缓冲区（Buffer）: 负责数据的存取
 * 3. 选择器（Selector) : 是 SelectableChannel 的多路复用器。用于监控 SelectableChannel 的 IO 状况。
 * @author zhouchaoqun
 * @date 2018/10/28 下午3:17
 */
public class NonBlockingNioTest {

    public static void main(String[] args) throws IOException {
        NonBlockingNioTest nonBlockingNioTest = new NonBlockingNioTest();
        nonBlockingNioTest.client();
    }

    /**
     * 聊天室客户端
     */
    @Test
    public void client() throws IOException {
        SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9898));
        socketChannel.configureBlocking(false);
        Scanner scanner = new Scanner(System.in);
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        while (scanner.hasNext()) {
            String content = scanner.nextLine();
            byteBuffer.put((LocalDateTime.now().toString() + "\n" + content).getBytes());
            byteBuffer.flip();
            socketChannel.write(byteBuffer);
            byteBuffer.clear();
        }
        scanner.close();
        socketChannel.close();
    }

    @Test
    public void server() throws IOException {
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.configureBlocking(false);
        serverSocketChannel.bind(new InetSocketAddress(9898));

        Selector selector = Selector.open();

        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

        while (selector.select() > 0) {
            Iterator<SelectionKey> keyIterator = selector.selectedKeys().iterator();
            while (keyIterator.hasNext()) {
                SelectionKey selectionKey = keyIterator.next();
                if (selectionKey.isAcceptable()) {
                    SocketChannel socketChannel = serverSocketChannel.accept();
                    socketChannel.configureBlocking(false);
                    socketChannel.register(selector, SelectionKey.OP_READ);
                } else if (selectionKey.isReadable()) {
                    SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
                    ByteBuffer byteBuffer = ByteBuffer.allocate(1024);

                    while (socketChannel.read(byteBuffer) > 0) {
                        byteBuffer.flip();
                        System.out.println(new String(byteBuffer.array(), byteBuffer.position(), byteBuffer.limit()));
                        byteBuffer.clear();
                    }
                }
                keyIterator.remove();
            }
        }
    }
}
